package edu.unicen.activateServices.activity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.IBinder;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.Toast;
import edu.unicen.activateServices.model.Rule;
import edu.unicen.activateServices.service.AndroidActivateServicesService;
import edu.unicen.activateServices.service.AndroidActivateServicesService.LocalBinder;
import edu.unicen.activateServices.utils.PersistenceData;

/**
 * @author <li>diegopiu</li> <li>silvinawesner</li>
 * 
 */
public class AndroidActivateServicesActivity extends Activity {

	public static String WINDOW_KEY = "WINDOW";

	public static String POLLING_TIME_KEY = "POLLING_TIME";

	public static String RULES = "RULES";

	/**
	 * Nombre del archivo en donde se guardan los últimos dominios utilizados.
	 */
	private static final String RULES_FILE = "rulesFile.dat";

	private HashMap<String, Rule> rules;

	private ArrayList<String> appsName;

	private Integer poolingTime;

	private Integer window;

	private AndroidActivateServicesService androidActivateServicesService;

	@SuppressWarnings("unused")
	private boolean myBound = false;

	/**
	 * Nombre del archivo donde se guarda la configuración.
	 */
	private static final String SETTINGS_FILE = "settings.dat";

	/**
	 * Componentes visuales para crear una regla
	 */
	private Button newButton;

	private Spinner appSpinner;

	private Button saveButton;

	private CheckBox checkBoxWifi;

	private CheckBox checkBoxGPS;

	/**
	 * Componentes visuales para visualizar reglas
	 */

	private ListView listRules;

	/**
	 * Componentes visuales para editar una regla
	 */

	private EditText editApp;

	private CheckBox editCheckBoxWifi;

	private CheckBox editCheckBoxGPS;

	private Button editButton;

	/**
	 * Componentes visuales para borrar una regla
	 */
	private Button deleteButton;

	private AlertDialog.Builder builder;

	/**
	 * Componentes visuales para la configuración
	 */
	private EditText poolingTimeEditText;

	private EditText relevanceWindowsEditText;

	private Button saveSettingsButton;

	/**
	 * Componentes visuales para cancelar una acción
	 */
	private Button cancelNewButton;//Para el formulario de alta de reglas
	
	private Button cancelEditButton;//Para el formulario de edición de reglas
	
	private Button cancelSettings;//Para el formulario de configuración
	
	
	/** Método invocado cuando la aplicación se inicia */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		inicializeApplicationData();
		loadSettings();
		bindService(this.getNewIntent(), myConnection, Context.BIND_AUTO_CREATE);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.menu, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.settings:
			setContentView(R.layout.settings);
			this.poolingTimeEditText = (EditText) findViewById(R.id.poolingTimeEditText);
			this.relevanceWindowsEditText = (EditText) findViewById(R.id.relevanceWindowsEditText);
			this.saveSettingsButton = (Button) findViewById(R.id.saveSettingsButton);
			this.cancelSettings = (Button) findViewById(R.id.cancelSettings);
			addListenerSaveSettingsButton();
			addListenerCancelSettingsButton();
			loadSettings();
			this.poolingTimeEditText.setText(this.poolingTime.toString());
			this.relevanceWindowsEditText.setText(this.window.toString());
			return true;
		case R.id.about:
			final AlertDialog alertDialog = new AlertDialog.Builder(
					AndroidActivateServicesActivity.this)
					.setTitle(R.string.appNameAndVersion)
					.setIcon(R.drawable.ic_launcher)
					.setMessage(R.string.aboutMessage).create();

			alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int which) {
					dialog.cancel();
				}
			});
			alertDialog.show();

			return true;
		case R.id.exit:
			new AlertDialog.Builder(this)
			.setTitle(R.string.exit)
			.setMessage(R.string.sureExitApp)
			.setNegativeButton(R.string.no, null)
			.setPositiveButton(R.string.yes,
					new DialogInterface.OnClickListener() {

						@Override
						public void onClick(DialogInterface dialog,
								int which) {
								System.exit(0);
						}
					}).create().show();
		default:
			return super.onOptionsItemSelected(item);
		}
	}

	@Override
	public void onBackPressed() {
	}

	@Override
	public void onStart() {
		super.onStart();
		loadApps();
		loadRules();
	}

	@Override
	protected void onResume() {
		super.onResume();
		addListenerNewRuleButton();
		addListenerEditRule();
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		unbindService(myConnection);
		myBound = false;
	}

	// *********Inicialización******************/
	/**
	 * Inicializa la lista de reglas existentes.
	 */
	private void inicializeApplicationData() {
		this.newButton = (Button) findViewById(R.id.newButton);
		
		this.listRules = (ListView) findViewById(R.id.listRules);
	}

	/**
	 * Inicializa el formulario de alta de una regla.
	 */
	private void inicializeFormNewRule() {

		this.appSpinner = (Spinner) AndroidActivateServicesActivity.this
				.findViewById(R.id.installedApplications);

		this.saveButton = (Button) findViewById(R.id.saveButton);

		this.checkBoxWifi = (CheckBox) findViewById(R.id.checkBoxWifi);

		this.checkBoxGPS = (CheckBox) findViewById(R.id.checkBoxGPS);
		
		this.cancelNewButton = (Button) findViewById(R.id.cancelNewButton);

	}

	/**
	 * Inicializa el formulario de edición de una regla.
	 */
	private void inicializeFormEditRule() {

		this.editApp = (EditText) AndroidActivateServicesActivity.this
				.findViewById(R.id.editApp);

		this.editButton = (Button) findViewById(R.id.editButton);

		this.editCheckBoxWifi = (CheckBox) findViewById(R.id.editCheckBoxWifi);

		this.editCheckBoxGPS = (CheckBox) findViewById(R.id.editCheckBoxGPS);

		this.deleteButton = (Button) findViewById(R.id.deleteButton);

		this.builder = new AlertDialog.Builder(this);
		
		this.cancelEditButton = (Button) findViewById(R.id.cancelEditButton);

	}

	// ***************** Listeners***************************/
	/**
	 * Define y ejecuta la funcionalidad del botón que permite acceder a crear
	 * una nueva regla.
	 */
	private void addListenerNewRuleButton() {

		newButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				setContentView(R.layout.rule);
				inicializeFormNewRule();
				loadApps();
				addListenerSaveButton();
				addListenerCancelNewButton();
			}
		});
	}

	/**
	 * Define y ejecuta la funcionalidad del botón que guarda una nueva regla.
	 */
	private void addListenerSaveButton() {

		saveButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				Rule rule = new Rule();
				rule.setName((String) appSpinner.getSelectedItem());
				List<String> services = new ArrayList<String>();
				if (checkBoxWifi.isChecked()) {
					services.add("wifi");
				}
				if (checkBoxGPS.isChecked()) {
					services.add("gps");
				}

				if (services.isEmpty()) {
					Toast.makeText(AndroidActivateServicesActivity.this,
							R.string.noServicesSelected, Toast.LENGTH_LONG)
							.show();
				} else {
					rule.setServices(services);
					addRule(rule);
				}
			}
		});
	}

	/**
	 * Define y ejecuta la funcionalidad del botón que permite acceder a editar
	 * una nueva regla.
	 */
	private void addListenerEditRule() {

		listRules.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				setContentView(R.layout.editrule);
				inicializeFormEditRule();
				String name = ((String) listRules.getItemAtPosition(position));
				Rule rule = rules.get(name);
				editApp.setText(name);
				editApp.setEnabled(false);
				editCheckBoxWifi.setChecked(rule.requireWifi());
				editCheckBoxGPS.setChecked(rule.requireGps());
				addListenerEditButton();
				addListenerDeleteRule();
				addListenerCancelEditButton();
			}
		});
	}

	/**
	 * Define y ejecuta la funcionalidad del botón que edita una regla.
	 */
	private void addListenerEditButton() {

		editButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				Rule rule = new Rule();
				rule.setName(editApp.getText().toString());
				List<String> services = new ArrayList<String>();
				if (editCheckBoxWifi.isChecked()) {
					services.add(Rule.WIFI);
				}
				if (editCheckBoxGPS.isChecked()) {
					services.add(Rule.GPS);
				}
				if (services.isEmpty()) {
					Toast.makeText(AndroidActivateServicesActivity.this,
							R.string.noServicesSelected, Toast.LENGTH_LONG)
							.show();
				} else {
					rule.setServices(services);
					addRule(rule);
				}
			}
		});
	}

	/**
	 * Define y ejecuta la funcionalidad del botón que permite eliminar una
	 * regla.
	 */
	private void addListenerDeleteRule() {

		deleteButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				builder.setMessage(R.string.sureDeleteRule)
						.setCancelable(false)
						.setPositiveButton(R.string.yes,
								new DialogInterface.OnClickListener() {
									public void onClick(DialogInterface dialog,
											int id) {
										String name = editApp.getText()
												.toString();
										removeRule(rules.get(name));
									}
								})
						.setNegativeButton(R.string.no,
								new DialogInterface.OnClickListener() {
									public void onClick(DialogInterface dialog,
											int id) {
										dialog.cancel();
									}
								});

				builder.create();
				builder.show();
			}
		});
	}

	/**
	 * Define y ejecuta la funcionalidad del botón que guarda las
	 * configuraciones. En el archivo se guardan separados por coma el tiempo de
	 * pooling y la ventana de relevancia.
	 */
	private void addListenerSaveSettingsButton() {

		saveSettingsButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				if (poolingTimeEditText.getText().toString().equals("")) {
					Toast.makeText(AndroidActivateServicesActivity.this,
							R.string.noPoolingTime, Toast.LENGTH_LONG).show();
				} else {
					poolingTime = new Integer(poolingTimeEditText.getText()
							.toString());
				}
				if (relevanceWindowsEditText.getText().toString().equals("")) {
					Toast.makeText(AndroidActivateServicesActivity.this,
							R.string.noRelevanceWindows, Toast.LENGTH_LONG)
							.show();
				} else {
					window = new Integer(relevanceWindowsEditText.getText()
							.toString());
				}
				updateFileSettings();
				setContentView(R.layout.main);
				inicializeApplicationData();
				loadRules();
				addListenerNewRuleButton();
				addListenerEditRule();
			}
		});
	}

	/**
	 * Define y ejecuta la funcionalidad del botón que permite cancelar el alta de una regla. 
	 */
	private void addListenerCancelNewButton() {

		cancelNewButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				setContentView(R.layout.main);
				inicializeApplicationData();
				loadRules();
				addListenerNewRuleButton();
				addListenerEditRule();
			}
		});
	}

	/**
	 * Define y ejecuta la funcionalidad del botón que permite cancelar la edición de una regla. 
	 */
	private void addListenerCancelEditButton() {

		cancelEditButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				setContentView(R.layout.main);
				inicializeApplicationData();
				loadRules();
				addListenerNewRuleButton();
				addListenerEditRule();
			}
		});
	}

	/**
	 * Define y ejecuta la funcionalidad del botón que permite cancelar la configuración. 
	 */
	private void addListenerCancelSettingsButton() {

		cancelSettings.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				setContentView(R.layout.main);
				inicializeApplicationData();
				loadRules();
				addListenerNewRuleButton();
				addListenerEditRule();
			}
		});
	}
	
	// ***********Manejo de datos**************/

	/**
	 * Recupera las aplicaciones instaladas
	 */
	private void loadApps() {
		this.appsName = new ArrayList<String>();
		PackageManager pm = getPackageManager();
		List<ApplicationInfo> packages = pm
				.getInstalledApplications(PackageManager.GET_META_DATA);
		for (ApplicationInfo ai : packages) {
			appsName.add(ai.processName);
		}
		ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,
				android.R.layout.simple_spinner_item, this.appsName);

		arrayAdapter
				.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		if (appSpinner != null) {
			appSpinner.setAdapter(arrayAdapter);
		}
	}

	/**
	 * Carga las reglas en el componente visual que las lista.
	 */
	public void loadRules() {
		this.rules = new HashMap<String, Rule>();
		String rulesFromFile = PersistenceData.readData(this,
				AndroidActivateServicesActivity.RULES_FILE);
		this.rules = stringToRules(rulesFromFile);

		String[] nameApps = new String[this.rules.size()];

		int i = 0;
		for (Iterator<String> iterator = this.rules.keySet().iterator(); iterator
				.hasNext();) {
			nameApps[i] = iterator.next();
			i++;
		}

		ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,
				android.R.layout.simple_list_item_1, android.R.id.text1,
				nameApps);

		this.listRules.setAdapter(arrayAdapter);
	}

	/**
	 * Recupera las reglas del archivo.
	 */
	public static HashMap<String, Rule> stringToRules(String allRules) {
		HashMap<String, Rule> rulesMap = new HashMap<String, Rule>();
		String[] rules = allRules.split(";");
		int i = 0;

		for (String ruleFromFile : rules) {
			if (i < rules.length - 1) {
				String[] r = ruleFromFile.split(",");
				Rule rule = new Rule();
				rule.setName(r[0]);
				i++;
				List<String> services = new ArrayList<String>();
				services.add(r[1]);
				if (r.length == 3) {
					services.add(r[2]);
				}
				rule.setServices(services);
				rulesMap.put(rule.getName(), rule);
			}
		}
		return rulesMap;
	}

	/**
	 * Actualiza el archivo de reglas a partir de las existentes en memoria.
	 */
	private void updateFileRules() {
		String stringFromFile = PersistenceData.readData(this, RULES_FILE);
		String[] savedRules = stringFromFile.split(",");

		List<String> sr = new ArrayList<String>();
		for (String string : savedRules) {
			if (!string.equals("")) {
				sr.remove(string);
			}
		}

		StringBuffer rulesToFile = new StringBuffer();

		for (Rule r : this.rules.values()) {
			rulesToFile.append(r.getName());
			if (r.requireWifi()) {
				rulesToFile.append("," + Rule.WIFI);
			}
			if (r.requireGps()) {
				rulesToFile.append("," + Rule.GPS);
			}
			rulesToFile.append(";");
		}

		PersistenceData.writeData(getApplicationContext(),
				rulesToFile.toString(),
				AndroidActivateServicesActivity.RULES_FILE);
	}

	/**
	 * Recupera las configuraciones del archivo y las carga en los componentes
	 * visuales.
	 */
	public void loadSettings() {
		String settingsFromFile = PersistenceData.readData(this,
				AndroidActivateServicesActivity.SETTINGS_FILE);
		String[] settings = settingsFromFile.split(",");

		if (settings.length == 1) {
			this.poolingTime = new Integer("10");
			this.window = new Integer("5");
		} else {
			this.poolingTime = new Integer(settings[0]);
			this.window = new Integer(settings[1]);
		}
	}

	/**
	 * Actualiza el archivo de configuraciones. En el archivo se guardan
	 * separados por coma el tiempo de pooling y la ventana de relevancia
	 */
	private void updateFileSettings() {
		List<String> sr = new ArrayList<String>();
		for (String string : PersistenceData.readData(this, SETTINGS_FILE)
				.split(",")) {
			if (!string.equals("")) {
				sr.remove(string);
			}
		}

		String settings = this.poolingTime.toString() + ","
				+ this.window.toString() + ",";
		PersistenceData.writeData(getApplicationContext(), settings,
				AndroidActivateServicesActivity.SETTINGS_FILE);
		androidActivateServicesService.setIntent(getNewIntent());
	}

	/**
	 * Agrega una regla a memoria, actualiza el archivo de reglas e inicializa
	 * la lista de reglas.
	 * 
	 * @param rule
	 */
	private void addRule(Rule rule) {
		if (this.rules == null) {
			this.rules = new HashMap<String, Rule>();
		}
		this.rules.put(rule.getName(), rule);
		this.updateFileRules();
		setContentView(R.layout.main);
		inicializeApplicationData();
		this.loadRules();
		addListenerNewRuleButton();
		addListenerEditRule();
		androidActivateServicesService.setIntent(getNewIntent());
	}

	/**
	 * Elimina una regla de memoria, actualiza el archivo de reglas e inicializa
	 * la lista de reglas.
	 * 
	 * @param rule
	 */
	private void removeRule(Rule rule) {
		this.rules.remove(rule.getName());
		this.updateFileRules();
		setContentView(R.layout.main);
		inicializeApplicationData();
		this.loadRules();
		addListenerNewRuleButton();
		addListenerEditRule();
		androidActivateServicesService.setIntent(getNewIntent());
	}

	/** Clase encargada de la comunicación entre el servicio y el activity */
	private ServiceConnection myConnection = new ServiceConnection() {

		@Override
		public void onServiceConnected(ComponentName className, IBinder service) {

			LocalBinder binder = (LocalBinder) service;
			androidActivateServicesService = binder.getService();
			myBound = true;
		}

		@Override
		public void onServiceDisconnected(ComponentName arg0) {
			myBound = false;
		}
	};

	private Intent getNewIntent() {
		Intent intent = new Intent(this, AndroidActivateServicesService.class);
		Bundle bundle = new Bundle();
		bundle.putInt(WINDOW_KEY, window);
		bundle.putInt(POLLING_TIME_KEY, poolingTime);
		bundle.putString(RULES, PersistenceData.readData(this,
				AndroidActivateServicesActivity.RULES_FILE));
		intent.putExtras(bundle);
		return intent;
	}

}